#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>

#include "../common/util.hpp"
#include "../common/log.hpp"

// 只负责进行代码的编译

namespace ns_compiler
{
    // 引入路径拼接的功能
    using namespace ns_util;
    // 引入日志功能
    using namespace ns_log;
    
    class Compiler
    {
    public:
        Compiler() {}
        ~Compiler() {}

        // 编译函数
        // 返回值true --> 编译成功
        // 返回值false --> 编译失败，错误信息存入临时文件中
        // 输入参数：待编译的文件名称(不带后缀)
        // 主进程继续执行编译操作，由子进程完成编译的工作
        static bool Compile(std::string& file_name)
        {
            pid_t pid = fork();
            if(pid < 0)
            {
                // fork 失败，直接返回false
                LOG(ERROR) << "内部错误，创建子进程失败！" << '\n';
                return false;
            }
            else if(pid == 0)
            {
                // 子进程：调用编译器，完成对代码的编译工作
                // 对于错误的文件，由标准错误重定向到我们自己的err临时文件
                // 先创建这个文件
                umask(0);
                int compile_errfd = ::open(PathUtil::CompileError(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
                if(compile_errfd < 0)
                {
                    // 创建文件失败，直接返回
                    LOG(WARNING) << "没有成功形成同名的err文件" << '\n';
                    exit(1);
                }
                // 从标准错误中重定向到我们自己的err临时文件
                dup2(compile_errfd, 2);
                // 进程程序替换，进行编译操作，注意进程程序替换不影响进程的文件描述符表
                // 注意要使用-D 宏定义 COMPILER_ONLINE，因为后来需要链接用户代码与测试代码，用来后台测试Judge，防止编译错误
                // g++ -o "exe" "src" -std=c++11 -D COMPILER_ONLINE
                execlp("g++", "g++", "-o", PathUtil::Exe(file_name).c_str(), PathUtil::Src(file_name).c_str(), "-std=c++11","-D", "COMPILER_ONLINE", nullptr /*没有的时候一定不要忘记加null作为结尾*/);
                LOG(ERROR) << "启动编译g++失败，可能是因为没有安装g++或者参数错误" << '\n';
                exit(2);
            }
            else
            {
                // 父进程：继续执行
                // 等待子进程
                waitpid(pid, nullptr, 0);
                // 检测编译是否成功，就看是否生成了对应的可执行程序.exe
                if(FileUtil::IsFileExists(PathUtil::Exe(file_name)))
                {
                    LOG(INFO) << PathUtil::Src(file_name).c_str() << " 编译成功！" << '\n';
                    return true;
                }
            }
            LOG(ERROR) << "编译失败，没有形成可执行程序" << '\n';
            return false;
        }
    };
}